#include <windows.h>
#include <aclapi.h>

#include "lib.h"
#include "patch.h"
#include "admpipe.h"
#include "casbase.h"
#include "log.h"

#include "admlogin.h"
#include "admlist.h"
#include "admadd.h"
#include "admrm.h"
#include "admupd.h"

#define SELF	AdminPipe
#define SUPER	NamedPipe


typedef struct _SecurityInfo	// passed from/to init/destroySecurity
{
	PSID pAdminSID;
	PACL pACL;
	PSECURITY_DESCRIPTOR pSD;
	SECURITY_ATTRIBUTES sa;
} SecurityInfo;


AdminPipe::AdminPipe()
	: NamedPipe()
{
	protocol[LOGIN] = new AdminLogin(this);
	protocol[LIST] = new AdminListPkg(this);
	protocol[ADD] = new AdminAddPkg(this);
	protocol[REMOVE] = new AdminRemovePkg(this);
	protocol[UPDATE] = new AdminUpdatePkg(this);
}

AdminPipe::~AdminPipe()
{
	int32 i;

	for (i = 0; i < NUMPROTOCOLS; i++)
	{
		delete protocol[i];
		protocol[i] = (AdminProtocol*)0;
	}
}

bool8
AdminPipe::create()
{
	bool8 result = false;
	SecurityInfo* si;
	String pipename;

	CasinoBase::load();
	pipename.sprintf(L"\\\\.\\pipe\\rtg%spatch", (const wchar_t*)CasinoBase::basename);

	si = (SecurityInfo*)initSecurity();
	if (si)
	{
		result = SUPER::create(pipename, 10000, &si->sa);
		destroySecurity(si);
	}

	return result;
}

bool8
AdminPipe::initOk()
{
	return (bool8)(0 !=
		(protocol[LOGIN]	&&
		protocol[LIST]		&&
		protocol[ADD]		&&
		protocol[REMOVE]	&&
		protocol[UPDATE]	&&
		SUPER::initOk()));
}

void
AdminPipe::msgStart()
{
	msglen = 1;			// reserve first byte to hold msglen
}

void
AdminPipe::msgAdd(uint8 Byte)
{
	obuf[oused + msglen++] = Byte;
}

uint8*
AdminPipe::msgGetBuf()
{
	return obuf + oused + msglen;
}

void
AdminPipe::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

bool8
AdminPipe::msgEnd()
{
	obuf[oused] = (uint8)(msglen & 0xff);
	oused += msglen;

	if (oused > BUFSIZE)
	{
		oused -= msglen;
		return false;
	}

	return true;
}

void
AdminPipe::msgStartUserBuilt()	// msglen assumed added by user
{
	msglen = 0;
}

void
AdminPipe::msgEndUserBuilt()	// msglen assumed added by user
{
	oused += msglen;
}

uint32
AdminPipe::msgGetRemain()
{
	return BUFSIZE - oused - msglen;		// return remaining space
}

void
AdminPipe::sendError(int32 Err)
{
	msgStart();
	msgAdd((uint8)'E');
	msgAdd((uint8)(Err & 0xff));
	msgEnd();
}

void
AdminPipe::connectionReset()
{
	int32 i;

	loggedOn = false;

	for (i = 0; i < NUMPROTOCOLS; i++)
		if (protocol[i])
			protocol[i]->reset();

	SUPER::connectionReset();
}

int32
AdminPipe::determineProtocol(uint8 Id)
{
	switch (Id)
	{
		case 'P':			// Pass
			return LOGIN;

		case 'L':
		case 'l':
			return LIST;

		case 'A':
			return ADD;

		case 'R':
			return REMOVE;

		case 'U':
			return UPDATE;

		default:
			Log::msgWarning("AdminPipe:  Unknown req (0x%02x)", Id);
			return -1;
	}
}

bool8
AdminPipe::getResponse()
{
	uint32 ieat = 0;
	int32 len, which;
	uint8* msg;

	while (ieat < iused)
	{
		msg = ibuf + ieat;
		len = msg[0];

		if (len == 0)				// bad msg...ignore everything else
		{
			ieat = iused;
			break;
		}
		else if (len == 1)
		{
			++ieat;
			continue;
		}

		if (ieat + len > iused)		// partial msg shouldn't happen
			break;

		//
		// At this point we have a msg at least 2 bytes long; len it total
		// number of bytes including the msglen byte.
		//
		// For convenience, make msg point to msg data (past msglen byte) and
		// update len to not include the msglen byte (just the remaining data).
		//
		ieat += len--;
		++msg;

		which = determineProtocol(msg[0]);

		if (which >= 0 && which < NUMPROTOCOLS && protocol[which])
			protocol[which]->handle(msg, len);
		else
			sendError(APE_UNKNOWN_REQUEST);
	}

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else if (ieat > 0)
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	return (bool8)(oused != 0);
}

void*
AdminPipe::initSecurity()
{
	DWORD dwRes;
	SecurityInfo* si;
	EXPLICIT_ACCESS ea;
	SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;

	si = (SecurityInfo*)LocalAlloc(LPTR, sizeof(SecurityInfo));
	if (!si)
	{
		Log::msgError("AdminPipe::initSecurity failed to alloc SecurityInfo");
		return (void*)0;
	}

	// Create a SID for the BUILTIN\Administrators group.
	//
	if (!AllocateAndInitializeSid(&SIDAuthNT, 2,
		SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
		&si->pAdminSID))
	{
		Log::msgError("AdminPipe::initSecurity AllocAndInitSid Adm error %u",
			GetLastError());
		LocalFree(si);
		return (void*)0;
	}

	// Initialize an EXPLICIT_ACCESS structure for an ACE.
	// The ACE will allow the Administrators group full access to the object.
	//
	ea.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
	ea.grfAccessMode = SET_ACCESS;
	ea.grfInheritance= NO_INHERITANCE;
	ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
	ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
	ea.Trustee.ptstrName  = (LPTSTR)si->pAdminSID;

	// Create a new ACL that contains the new ACEs.
	//
	dwRes = SetEntriesInAcl(1, &ea, NULL, &si->pACL);
	if (ERROR_SUCCESS != dwRes)
	{
		Log::msgError("AdminPipe::initSecurity SetEntriesInAcl error %u",
			GetLastError());
		FreeSid(si->pAdminSID);
		LocalFree(si);
		return (void*)0;
	}

	// Initialize a security descriptor.  
 	//
	si->pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
		SECURITY_DESCRIPTOR_MIN_LENGTH); 
	if (!si->pSD)
	{
		Log::msgError("AdminPipe::initSecurity LocalAlloc error %u",
			GetLastError());
		LocalFree(si->pACL);
		FreeSid(si->pAdminSID);
		LocalFree(si);
		return (void*)0;
	} 
 
	if (!InitializeSecurityDescriptor(si->pSD, SECURITY_DESCRIPTOR_REVISION))
	{
		Log::msgError("AdminPipe::initSecurity InitializeSecurityDescriptor "
			"error %u", GetLastError());
		LocalFree(si->pSD);
		LocalFree(si->pACL);
		FreeSid(si->pAdminSID);
		LocalFree(si);
		return (void*)0;
	} 
 
	// Add the ACL to the security descriptor. 
	//
	if (!SetSecurityDescriptorDacl(si->pSD, 
		TRUE,     // fDaclPresent flag   
		si->pACL, 
		FALSE))   // not a default DACL 
	{  
		Log::msgError("AdminPipe::initSecurity SetSecurityDescriptorDacl"
			" error %u", GetLastError());
		LocalFree(si->pSD);
		LocalFree(si->pACL);
		FreeSid(si->pAdminSID);
		LocalFree(si);
		return (void*)0;
	} 

	// Initialize a security attributes structure.
	//
	si->sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	si->sa.lpSecurityDescriptor = si->pSD;
	si->sa.bInheritHandle = FALSE;

	return si;
}

void
AdminPipe::destroySecurity(void* security)
{
	SecurityInfo* si = (SecurityInfo*)security;

	if (si)
	{
		if (si->pAdminSID) 
			FreeSid(si->pAdminSID);

		if (si->pACL) 
			LocalFree(si->pACL);

		if (si->pSD) 
			LocalFree(si->pSD);

		LocalFree(si);
	}
}

void
AdminPipe::msgAdd(uint8* buf, int32 len)
{
	memcpy(obuf + oused + msglen, buf, len);
	msglen += len;
}
/*
void
AdminPipe::msgAdd(const String& str)
{
//	memcpy(obuf + oused + msglen, buf, len);
//	msglen += len;
	msglen += str.serializeAnsi(obuf + oused + msglen);
//	msglen += str.serialize(obuf + oused + msglen);
}
*/
void
AdminPipe::msgAdd(const String& str, bool8 unicode)
{
	if (unicode)
	{
		msglen += str.serialize(obuf + oused + msglen);
	}
	else
	{
		int len = str.getLength();
		char* buf;

		// The code base is unicode, but we still only xfer ASCII across the wire
		buf = new char[len+1];
		StringConversions::convert(buf, str, len+1);
		msgAdd((uint8)(len & 0xff));
		msgAdd((uint8*)buf, len);

		delete buf;
	}
}

